/* eslint-disable no-console */
import { join } from 'path';
import { readdirSync, lstatSync } from 'fs';
import chalk from 'chalk';
import { config as _config } from './qiniu-config.js';
import { fileURLToPath } from 'url';  
import { dirname } from 'path';  
import qiniu from 'qiniu'


const { auth, conf, zone, rs, form_up } = qiniu
const { blue, red, green, yellow } = chalk; // 颜色模块

const __filename = fileURLToPath(import.meta.url);  
const __dirname = dirname(__filename);  

const { ak, sk, bucket } = _config;

const mac = new auth.digest.Mac(ak, sk); //生成鉴权对象

// 构建配置类
const config = new conf.Config();
config.zone = zone['cn-east-2']; // 对应地区配置 https://developer.qiniu.com/kodo/4088/s3-access-domainname
config.useCdnDomain = true; // 上传是否使用cdn加速
// 生成资源管理对象
const bucketManager = new rs.BucketManager(mac, config);

/**
 * 上传文件方法
 * @param key 文件名
 * @param file 文件路径
 * @returns {Promise<unknown>}
 */
const doUpload = (key, file) => {
  console.log(blue(`正在上传：${file}`));
  // 凭证配置
  const options = {
    scope: `${bucket}:${key}`, // :key用于覆盖上传，除了需要简单上传所需要的信息之外，还需要想进行覆盖的文件名称，这个文件名称同时可是客户端上传代码中指定的文件名，两者必须一致
    expires: 7200 // 凭证有效期
  };
  const formUploader = new form_up.FormUploader(config);
  const putExtra = new form_up.PutExtra();
  const putPolicy = new rs.PutPolicy(options);
  const uploadToken = putPolicy.uploadToken(mac); // 上传凭证
  return new Promise((resolve, reject) => {
    formUploader.putFile(uploadToken, key, file, putExtra, (err, body, info) => {
      if (err) {
        reject(err);
      }
      if (info.statusCode === 200) {
        resolve(body);
      } else {
        reject(body);
      }
    });
  });
};

const getBucketFileList = (callback, marker, list = []) => {
  !marker && console.log(blue('正在获取空间文件列表'));
  // @param options 列举操作的可选参数
  //                prefix    列举的文件前缀
  //                marker    上一次列举返回的位置标记，作为本次列举的起点信息
  //                limit     每次返回的最大列举文件数量
  //                delimiter 指定目录分隔符
  const options = {
    limit: 100,
  };
  if (marker) {
    options.marker = marker;
  }
  // 获取指定前缀的文件列表
  bucketManager.listPrefix(bucket, options, (err, respBody, respInfo) => {
    if (err) {
      console.log(red(`获取空间文件列表出错 ×`));
      console.log(red(`错误信息：${JSON.stringify(err)}`));
      throw err;
    }
    if (respInfo.statusCode === 200) {
      // 如果这个nextMarker不为空，那么还有未列举完毕的文件列表，下次调用listPrefix的时候，
      // 指定options里面的marker为这个值
      const nextMarker = respBody.marker;
      const { items } = respBody;
      const newList = [...list, ...items];
      if (!nextMarker) {
        console.log(green(`获取空间文件列表成功 ✓`));
        console.log(blue(`需要清理${newList.length}个文件`));
        callback(newList);
      } else {
        getBucketFileList(callback, nextMarker, newList);
      }
    } else {
      console.log(yellow(`获取空间文件列表异常 状态码${respInfo.statusCode}`));
      console.log(yellow(`异常信息：${JSON.stringify(respBody)}`));
    }
  });
};

const clearBucketFile = () =>
  new Promise((resolve, reject) => {
    getBucketFileList(items => {
      if (!items.length) {
        resolve();
        return;
      }
      console.log(blue('正在清理空间文件'));
      const deleteOperations = [];
      // 每个operations的数量不可以超过1000个，如果总数量超过1000，需要分批发送
      items.forEach(item => {
        deleteOperations.push(rs.deleteOp(bucket, item.key));
      });
      bucketManager.batch(deleteOperations, (err, respBody, respInfo) => {
        if (err) {
          console.log(red(`清理空间文件列表出错 ×`));
          console.log(red(`错误信息：${JSON.stringify(err)}`));
          reject();
        } else if (respInfo.statusCode >= 200 && respInfo.statusCode <= 299) {
          console.log(green(`清理空间文件成功 ✓`));
          resolve();
        } else {
          console.log(yellow(`获取空间文件列表异常 状态码${respInfo.deleteusCode}`));
          console.log(yellow(`异常信息：${JSON.stringify(respBody)}`));
          reject();
        }
      });
    });
  });

const publicPath = join(__dirname, '../dist');

const uploadAll = async (dir, prefix) => {
  if (!prefix){
    console.log(blue('执行清理空间文件'));
    await clearBucketFile();
    console.log(blue('正在读取打包文件'));
  }
  const files = readdirSync(dir);
  if (!prefix){
    console.log(green('读取成功 ✓'));
    console.log(blue('准备上传文件'));
  }
  files.forEach(file => {
    const filePath = join(dir, file);
    const key = prefix ? `${prefix}/${file}` : file;
    if (lstatSync(filePath).isDirectory()) {
      uploadAll(filePath, key);
    } else {
      doUpload(key, filePath)
        .then(() => {
          console.log(green(`文件${filePath}上传成功 ✓`));
        })
        .catch(err => {
          console.log(red(`文件${filePath}上传失败 ×`));
          console.log(red(`错误信息：${JSON.stringify(err)}`));
          console.log(blue(`再次尝试上传文件${filePath}`));
          doUpload(file, filePath)
            .then(() => {
              console.log(green(`文件${filePath}上传成功 ✓`));
            })
            .catch(err2 => {
              console.log(red(`文件${filePath}再次上传失败 ×`));
              console.log(red(`错误信息：${JSON.stringify(err2)}`));
              throw new Error(`文件${filePath}上传失败，本次自动化构建将被强制终止`);
            });
        });
    }
  });
};

uploadAll(publicPath).finally(() => {
  console.log(green(`上传操作执行完毕 ✓`));
  console.log(blue(`请等待确认所有文件上传成功`));
});
